﻿using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Red.Controls.Diagrams.Declarators;
using Red.Controls.ViewModels;

namespace Red.Controls.Diagrams.InteropUtils
{
    public class InteropObjectInitializer
    {
        #region Data

        private Dictionary<string, InteropProperty> _properties = new Dictionary<string, InteropProperty>();
        private DataItem _dataItem;
        private readonly NodeDeclarators _declarators;
        private readonly List<string> _bindingProperties = new List<string>(); // properties that were specified in bindings in NodeDeclarators

        #endregion Data

        #region C-tor

        public InteropObjectInitializer( NodeDeclarators declarators )
        {
            _declarators = declarators;
        }

        #endregion C-tor

        #region Public methods

        public async Task<Dictionary<string, InteropProperty>> Visit( DataItem dataItem )
        {
            _dataItem = dataItem;
            await GetEditorOnlyProperties();
            return _properties;
        }

        #endregion Public methods

        #region Private methods

        private async Task GetEditorOnlyProperties()
        {
            var tasks = new List<Task>(5);

            tasks.Add( GetPropertyValue( "Class" ) );
            tasks.Add( GetPropertyValue( "Id" ) );
            tasks.Add( GetPropertyValue( "X", "properties.X" ) );
            tasks.Add( GetPropertyValue( "Y", "properties.Y" ) );
            tasks.Add( GetPropertyValue( "Comment", "properties.Comment" ) );

            await Task.WhenAll( tasks );
        }

        private async Task GetPropertyValue( string propertyName, string fullPath = null )
        {
            var propertyInitializer = new InteropPropertyInitializer( propertyName, _dataItem, fullPath );
            var propertyDataItem = await propertyInitializer.Execute();

            if ( propertyDataItem.Title == "Class" )
            {
                var className = propertyDataItem.Value;
                var nodeDeclarator = _declarators.GetNodeDeclarator( className );
                if ( nodeDeclarator != null )
                    await InitializeBindings( nodeDeclarator );
            }

            _properties.Add( propertyName, new InteropEditorOnlyProperty( propertyName, propertyDataItem ) );
        }

        private async Task InitializeBindings( NodeDeclarator declarator )
        {
            _bindingProperties.AddRange( GetBindingProperties( declarator ) );
            var tasks = _bindingProperties.Select( async bindingProperty => await GetBindingPropertyValue( bindingProperty ) );
            await Task.WhenAll( tasks );
        }

        private IEnumerable<string> GetBindingProperties( NodeDeclarator declarator )
        {
            var inputsWithBindings = declarator.Inputs.Where( item => !string.IsNullOrEmpty( item.Binding ) ).Select( item => item.Binding );
            var outputsWithBindings = declarator.Outputs.Where( item => !string.IsNullOrEmpty( item.Binding ) ).Select( item => item.Binding );

            var bindingProperties = new List<string>();
            bindingProperties.AddRange( inputsWithBindings );
            bindingProperties.AddRange( outputsWithBindings );

            return bindingProperties;
        }

        private async Task GetBindingPropertyValue( string bindingProperty )
        {
            var bindingPropertyValue = await _dataItem.ExecuteAsync<string>( "readProperty", bindingProperty );
            var bindingPropertyType = await _dataItem.ExecuteAsync<string>( "getPropertyType", bindingProperty );
            _properties.Add( bindingProperty, new InteropBindingProperty( bindingProperty, bindingPropertyValue.Value, _dataItem, bindingPropertyType.Value ) );
        }

        #endregion Private methods
    }
}
